#include <iostream>
#include "test_utilities.hpp"
#include "../list.hpp"
#include "list_test.hpp"
using std::cout;
using std::endl;

namespace listTest {

const int dummyValue = 1;
const int dummyRange = 10;

void testCreate();
void testInsert();
void testRemove();
void testIteratorRemove();
void testFind();
void testIterators();

void run_list_tests() {
	RUN_TEST(testCreate);
	RUN_TEST(testInsert);
	RUN_TEST(testRemove);
	RUN_TEST(testIteratorRemove);
	RUN_TEST(testFind);
	RUN_TEST(testIterators);
}

void testCreate() {
	ASSERT_NO_THROW(List<int>());
}

void testInsert() {
	List<int> list;
	ASSERT_NO_THROW(list.insertFirst(dummyValue));
	List<int>::Iterator iterator = list.begin();
	ASSERT_EQUALS(*iterator, dummyValue);
	ASSERT_EQUALS(list.end(), ++iterator);
}

void testRemove() {
	List<int> list;
	list.insertFirst(dummyValue);
	list.insertFirst(dummyValue + 1);
	list.remove(list.begin());
	ASSERT_EQUALS(dummyValue, *(list.begin()));
}

void testIteratorRemove() {
	List<int> list;
	list.insertFirst(3);
	List<int>::Iterator thirdIterator = list.begin();
	list.insertFirst(2);
	List<int>::Iterator secondIterator = list.begin();
	list.insertFirst(1);
	List<int>::Iterator firstIterator = list.begin();

	ASSERT_EQUALS(1, *(list.begin()));
	ASSERT_NO_THROW(secondIterator.removeFromList());
	ASSERT_EQUALS(1, *(list.begin()));
	ASSERT_NO_THROW(firstIterator.removeFromList());
	ASSERT_EQUALS(3, *(list.begin()));
	ASSERT_NO_THROW(thirdIterator.removeFromList());
	ASSERT_EQUALS(list.end(), list.begin());
	ASSERT_THROWS(NoElementException, list.begin().removeFromList());
}

class BiggerThan {
	int num;
public:
	BiggerThan(int num) : num(num) {}
	bool operator()(int otherNum) const {
		return otherNum > num;
	}
};

void testFind() {
	List<int> list;
	for (int i = dummyValue + dummyRange; i >= dummyValue; --i) {
		list.insertFirst(i);
	}

	for (int i = dummyValue; i < dummyValue + dummyRange; ++i) {
		List<int>::Iterator iterator = list.find(BiggerThan(i));
		ASSERT_EQUALS(*iterator, i + 1);
	}
	List<int>::Iterator iterator = list.find<BiggerThan>(
			BiggerThan(dummyValue + dummyRange));
	ASSERT_EQUALS(iterator, list.end());

	const List<int> constList = list;

	for (int i = dummyValue + dummyRange - 1; i >= dummyValue; --i) {
		List<int>::ConstIterator cIterator = \
				constList.find<BiggerThan>(BiggerThan(i));
		ASSERT_EQUALS(*cIterator, i + 1);
	}
	List<int>::ConstIterator cIterator = constList.find<BiggerThan>(
			BiggerThan(dummyValue + dummyRange));
	ASSERT_EQUALS(cIterator, constList.end());
}

void testIterators() {
	List<int> list;
	List<int> otherList;

	List<int>::Iterator iterator = list.begin();
	List<int>::Iterator otherIterator = otherList.begin();

	ASSERT_THROWS(NonMatchingListException, list.remove(otherIterator));

	ASSERT_FALSE(iterator == otherIterator);

	List<int>::ConstIterator constIterator = iterator;
	ASSERT_TRUE(constIterator == iterator);

	for (int i = dummyValue; i < dummyValue + dummyRange; ++i) {
		list.insertFirst(i);
	}

	int i = dummyValue;
	for (List<int>::ConstIterator constIterator = list.rbegin();
			list.rend() != constIterator;
			--constIterator) {
		ASSERT_EQUALS(*constIterator, i);
		++i;
	}
}

}
